#include "StdAfx.h"
#include "DefenderMachine.h"

void CDefenderMachine::WritePalette(u32 addr,u8 data)
{
Palette[addr & 0xF] = ConvertBBGGGRRR(data);
}

u8 *_gramptr;

CDefenderMachine::CDefenderMachine()
{
_gramptr = MainRAM;

CPU[0] = new CDead6809();
CPU[1] = new CDead6808();
MainCPU = (CDead6809*)CPU[0];
SoundCPU = (CDead6808*)CPU[1];
Watchdog = new CWatchdog(this,1,180);		//what is a good value for watchdog?
PIA[0] = new C6821();
PIA[1] = new C6821();
PIA[2] = new C6821();
DAC = new CDAC();
AddLoadInfo(ROM_CPU1,MainROM);
AddLoadInfo(ROM_CPU2,SoundROM);
AddLoadInfo(ROM_PROM,PROM);
}

CDefenderMachine::~CDefenderMachine()
{
delete PIA[0];
delete PIA[1];
delete PIA[2];
delete DAC;
}

int CDefenderMachine::Init()
{
int i;

for(i=0;i<0x2800;i++)
	{
	NVRAM[i & 0x3FF] = 0;
	MainRAM[i] = 0;
	}
Sound->SetUser(this);
MainCPU->Init();
SoundCPU->Init();
MainCPU->SetUser(this);
SoundCPU->SetUser(this);
MainCPU->SetOp(ReadMainCPU);
MainCPU->SetRead(ReadMainCPU);
MainCPU->SetWrite(WriteMainCPU);
SoundCPU->SetOp(ReadSoundCPU);
SoundCPU->SetRead(ReadSoundCPU);
SoundCPU->SetWrite(WriteSoundCPU);
PIA[0]->SetUser(this);
PIA[1]->SetUser(this);
PIA[2]->SetUser(this);
PIA[0]->InputAFunc = PIA0_InputA;
PIA[0]->InputBFunc = PIA0_InputB;
PIA[1]->InputAFunc = PIA1_InputA;
PIA[1]->OutputBFunc = PIA1_OutputB;
PIA[1]->IRQAFunc = PIA1_IRQA;
PIA[1]->IRQBFunc = PIA1_IRQB;
PIA[2]->OutputAFunc = PIA2_OutputA;
PIA[2]->IRQAFunc = PIA2_IRQA;
PIA[2]->IRQBFunc = PIA2_IRQB;
sNVRAM = new CNVRAM(RomSet->GetName(),NVRAM,0x400);		//setup nvram saving/loading
return(0);
}

void CDefenderMachine::Kill()
{
while(SoundWrites.num())
	SoundWrites.del_savedata(0);
}

/*
hack to decrypt defndjeu/zero/others
*/
static void Decrypt(u8 *dest,u8 *src,int len)
{
int i;

for(i=0;i<len;i++)
	{
	u8 b = src[i];
	
	dest[i] = ((b & 0x80) >> 7) | ((b & 0x01) << 7) | (b & 0x7E);
	}
}

void CDefenderMachine::Reset()
{
int i,n;
CRom *Rom;

for(n=0;n<0x10000;n++)
	MainROM[n] = SoundROM[n] = 0;
LoadROMs();
for(n=RomSet->Num(),i=0;i<n;i++)
	{
	Rom = RomSet->GetROM(i);
	if((Rom->Flags & ROM_CPU1) && (Rom->Flags & ROM_DECRYPT))
		Decrypt(MainROM + Rom->LoadAddr,Rom->Data,Rom->Size);
	}
for(n=0;n<0x9800;n++)
	VRAM[n] = 0;
for(n=0;n<0x2800;n++)
	MainRAM[n] = 0;
for(n=0;n<0x80;n++)
	SoundRAM[n] = 0;
Sound->SetCallback(ProcessSound);
BankRegister = 0;
BankPointer = 0;
Scanline = 0;
MainCPU->Reset();
SoundCPU->Reset();
Watchdog->Reset();
PIA[0]->Reset();
PIA[1]->Reset();
PIA[2]->Reset();
DAC->Reset();
Watchdog->Enable();
if(RomSet->GetFlags() & F_NOWATCHDOG)
	Watchdog->Disable();
}

void CDefenderMachine::Blit()
{
Video->StartBlit();
RenderScreen(Screen,304);
BlitRotate(Screen,(u32*)Video->GetSurface(),Palette);
Video->StopBlit();
}

void CDefenderMachine::Line(u32 mainc,u32 soundc,int irq)
{
int i,j;

while(SoundWrites.num())
	{
	u32 Data = (u32)SoundWrites.get(0);

	if(Data & 0x10000)					//flag for williams2 sound writes
		PIA[2]->SetInputA((u8)Data);
	else
		{
		PIA[2]->SetInputB((u8)Data);
		PIA[2]->set_input_cb1(((u8)Data == 0xFF) ? 0 : 1);
		}
	SoundWrites.del_savedata(0);
	}
if((Scanline & 0xF) == 0x0)//0xF)
	PIA[1]->set_input_cb1(Scanline & 0x20);
if(Scanline == 240)
	PIA[1]->set_input_ca1(1);
MainCPU->Execute(mainc);
SoundCPU->Execute(soundc);
Scanline++;
i = 44100 / 60 * (Scanline - 1) / 272;			//calculate the number of samples per scanline
j = 44100 / 60 * Scanline / 272;
DAC->UpdateSamples(j - i);							//and generate them
if(Scanline == 256)
	Blit();
if(Scanline == 272)
	{
	ProcessInput();
	Watchdog->Frame();
	PIA[1]->set_input_ca1(0);
	Scanline = 0;
	}
}

u32 frame=0;

void CDefenderMachine::Frame()
{
int i;

/*
main cpu is 1mhz, that is 16666.66667 cycles per frame
sound cpu is 894,750hz, that is 14912.5 cycles per frame

how many scanlines per frame?  how long is vblank?
*/
for(i=0;i<272;i++)
	Line(62,55,0);
frame++;
}

void CDefenderMachine::SaveState(CState *s)
{
MainCPU->SaveState(s);
SoundCPU->SaveState(s);
s->Write(VRAM,0x9800);
s->Write(MainRAM,0x2800);
s->Write(NVRAM,0x400);
s->Write(PaletteRAM,0x10);
s->Write8(BankRegister);
s->Write8(InputData[0]);
s->Write8(InputData[1]);
s->Write8(InputData[2]);
s->Write32((u32)Scanline);
s->Write(Screen,304*256);				//screen saved...bad...but blitter may have blitted
}

void CDefenderMachine::LoadState(CState *s)
{
int i;

MainCPU->LoadState(s);
SoundCPU->LoadState(s);
s->Read(VRAM,0x9800);
s->Read(MainRAM,0x2800);
s->Read(NVRAM,0x400);
s->Read(PaletteRAM,0x10);
BankRegister = s->Read8();
InputData[0] = s->Read8();
InputData[1] = s->Read8();
InputData[2] = s->Read8();
Scanline = (int)s->Read32();
for(i=0;i<16;i++)							//restore palette
	WritePalette(i,PaletteRAM[i]);
WriteMainCPU((void*)this,0xD000,BankRegister);	//restore bank pointer
s->Read(Screen,304*256);				//screen saved...bad...but blitter may have blitted
}

void CDefenderMachine::RenderScreen(u8 *dest,int pitch)
{
int x,y;
u8 *Src,*Dest;

Src = VRAM;
Dest = Screen;
for(y=0;y<256;y++)
	{
	for(x=0;x<304;)
		{
		u8 b = Src[(x / 2) * 256];
		
		Dest[x++] = b >> 4;
		Dest[x++] = b & 0xF;
		}
	Dest += pitch;
	Src++;
	}
}

void CDefenderMachine::ProcessSound(void *user,void *data,int len)
{
s16 *dest = (s16*)data;

if(user)
	((CDefenderMachine*)user)->DAC->Update(0,dest,len);
}

u8 CDefenderMachine::ReadMainCPU(void *user,u32 addr)
{
CDefenderMachine *M = (CDefenderMachine*)user;

switch(addr & 0xF000)
	{
	case 0x0000:
	case 0x1000:
	case 0x2000:
	case 0x3000:
	case 0x4000:
	case 0x5000:
	case 0x6000:
	case 0x7000:
	case 0x8000:
		return(M->VRAM[addr]);
	case 0x9000:
		if(addr >= 0x9800)
			return(M->MainRAM[addr - 0x9800]);
		return(M->VRAM[addr]);
	case 0xA000:
		if(M->RomSet->GetFlags() & F_MAYDAYPROTECTION)	//hack? for mayday's protection
			{
			if(addr == 0xA190 || addr == 0xA191)
				return(M->MainRAM[(addr + 3) - 0x9800]);
			}
	case 0xB000:
		return(M->MainRAM[addr - 0x9800]);
	case 0xC000:
		if(M->BankPointer)
			return(M->BankPointer[addr & 0xFFF]);
		switch(addr & 0xCF00)
			{
			case 0xC400:
				return(M->NVRAM[addr & 0xFF]);
			case 0xC800:
			case 0xC900:
			case 0xCA00:
			case 0xCB00:
				return((u8)M->Scanline);
			}
		if((addr & 0xCF00) == 0xC400)
			return(M->NVRAM[addr & 0xFF]);
		if((addr & 0xFFF8) == 0xCC00)		//pia read
			{
			if(addr & 4)		//pia 0 read
				return(M->PIA[0]->Read(addr & 3));
			else					//pia 1 read
				return(M->PIA[1]->Read(addr & 3));
			}
		break;
	case 0xD000:							//these are hardwired, the rest are selectable
	case 0xE000:
	case 0xF000:
		return(M->MainROM[addr]);
	}
message("unhandled read (main cpu, addr $%04X)\n",addr);
return(0);
}

void CDefenderMachine::WriteMainCPU(void *user,u32 addr,u8 data)
{
CDefenderMachine *M = (CDefenderMachine*)user;
u8 Bank;

switch(addr & 0xF000)
	{
	case 0x0000:
	case 0x1000:
	case 0x2000:
	case 0x3000:
	case 0x4000:
	case 0x5000:
	case 0x6000:
	case 0x7000:
	case 0x8000:
		M->VRAM[addr] = data;
		break;
	case 0x9000:
		if(addr < 0x9800)
			{
			M->VRAM[addr] = data;
			return;
			}
	case 0xA000:
	case 0xB000:
		M->MainRAM[addr - 0x9800] = data;
		break;
	case 0xC000:							//palette/nvram/io
		if(M->BankPointer)
			{
			message("write rejected, $%04X = $%02X\n",addr,data);
			return;
			}
		if(addr == 0xC3FC) 				//watchdog
			{
			if((data & 0x38) == 0x38)			//check for specific value before allowing write
				M->Watchdog->Write(W_CPU1);	//jin writes 0x39
			else
				message("bad watchdog write = $%02X\n",data);
			return;
			}
		if(addr < 0xC010)					//palette write
			{
			M->PaletteRAM[addr & 0xF] = data;
			M->WritePalette(addr,data);
			return;
			}
		if((M->BankRegister & 0xF) == 0)	//io mapped in
			{
			if((addr & 0xFF00) == 0xC400)	//nvram write
				M->NVRAM[addr & 0xFF] = data | 0xF0;
			else if((addr & 0xFFF8) == 0xCC00)	//pia write
				{
				if(addr & 4)		//pia 0 write
					M->PIA[0]->Write(addr & 3,data);
				else					//pia 1 write
					M->PIA[1]->Write(addr & 3,data);
				}
			}
		break;
	case 0xD000:							//bank select
		M->BankRegister = data;
		Bank = data & 7;
		switch(Bank)
			{
			case 0:							//map to io space
				M->BankPointer = 0;
				break;
			case 1:							//map to rom
			case 2:
			case 3:
			case 4:
			case 5:
			case 6:
			case 7:
			case 8:
			case 9:
				M->BankPointer = M->MainROM + 0x10000 + ((Bank - 1) * 0x1000);
				break;
			}
		return;
	default:
		message("unhandled write (main cpu, addr $%04X = $%02X)\n",addr,data);
		break;
	}
}

u8 CDefenderMachine::ReadSoundCPU(void *user,u32 addr)
{
CDefenderMachine *M = (CDefenderMachine*)user;

if(addr < 0x100)
	return(M->SoundRAM[addr]);
if(addr >= 0x0400 && addr <= 0x0403)
	return(M->PIA[2]->Read(addr & 3));
if(addr >= 0xB000)
	return(M->SoundROM[addr]);
message("unhandled read (sound cpu, addr $%04X)\n",addr);
return(0);
}

void CDefenderMachine::WriteSoundCPU(void *user,u32 addr,u8 data)
{
CDefenderMachine *M = (CDefenderMachine*)user;

if(addr < 0x100)
	{
	M->SoundRAM[addr] = data;
	return;
	}
if(addr >= 0x0400 && addr <= 0x0403)
	{
	M->PIA[2]->Write(addr & 3,data);
	return;
	}
message("unhandled write (sound cpu, addr $%04X = $%02X)\n",addr,data);
}

u8 CDefenderMachine::PIA0_InputA(void *user)
{
CDefenderMachine *M = (CDefenderMachine*)user;

return(M->InputData[0]);
}

u8 CDefenderMachine::PIA0_InputB(void *user)
{
CDefenderMachine *M = (CDefenderMachine*)user;

return(M->InputData[1]);
}

u8 CDefenderMachine::PIA1_InputA(void *user)
{
CDefenderMachine *M = (CDefenderMachine*)user;

return(M->InputData[2]);
}

void CDefenderMachine::PIA1_OutputB(void *user,u8 data)
{
CDefenderMachine *M = (CDefenderMachine*)user;

M->SoundWrites.add((void*)(u32)(data | 0xC0));			//add this write to the write queue
}

void CDefenderMachine::PIA1_IRQA(void *user)
{
CDefenderMachine *M = (CDefenderMachine*)user;

M->MainCPU->IRQ();
}

void CDefenderMachine::PIA1_IRQB(void *user)
{
CDefenderMachine *M = (CDefenderMachine*)user;

M->MainCPU->IRQ();
}

void CDefenderMachine::PIA2_OutputA(void *user,u8 data)
{
CDefenderMachine *M = (CDefenderMachine*)user;

M->DAC->Write(0,data | 0xC0);
}

void CDefenderMachine::PIA2_IRQA(void *user)
{
CDefenderMachine *M = (CDefenderMachine*)user;

M->SoundCPU->IRQ();
}

void CDefenderMachine::PIA2_IRQB(void *user)
{
CDefenderMachine *M = (CDefenderMachine*)user;

M->SoundCPU->IRQ();
}
